
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <sstream>
#include <assert.h>
#include <libmemcached/memcached.h>
#include "configParser.h"
#include "Debug.h"
#include "Helper.h"
 
using namespace std;
Helper * Helper::_instance = NULL;

Helper::Helper() 
{
	m_sConfFile = "";
	g_expire = 86400;
	g_db = NULL;
	g_pDB = NULL;
        m_cobweb_port = 0;
}

Helper::~Helper()
{
     if(g_db != NULL){
                PQfinish( g_db);
                g_db = NULL;
        }
        if(g_pDB != NULL)
           delete g_pDB;
}

Helper * Helper::Instance(const char *confFile)
{
	if (NULL == _instance) 
	{
		_instance = new Helper();
		if ( !_instance->Startup(confFile) ) {
			delete _instance; 
			_instance = NULL;
		}
	}
	return _instance;
}

void Helper::Release(bool bForce)
{
	if (_instance != NULL) 
	{
		if(bForce)
		{
			delete _instance; 
			_instance = NULL;
		}
	}
}

bool  Helper::Startup(const char *confFile)
{
	if(confFile != NULL)
		m_sConfFile  = confFile;
	if(m_sConfFile.length() < 1)
	{
		ERRO<<": no helper.conf be given!"<<endl;
		return false;
	}
	struct stat st;
	if (lstat(confFile, &st) != 0)
	{
		ERRO<<":"<<confFile<<" is not exist!"<<endl;
		return false;
	}

	configParser cfg;
	map <string, string > g_Confs;
	g_Confs = cfg.Parser(confFile);
	if(g_Confs.size() <= 0)
	{
		cerr<<"error to explain conf :"<< confFile <<endl;
		return false;
	}

	map <string, string >::iterator iter;
	string dbhost, dbuser, dbpass, dbname;
	if (g_Confs.find("s_dbhost") != g_Confs.end())
		dbhost = g_Confs["s_dbhost"];
	if (g_Confs.find("s_dbuser") != g_Confs.end())
		dbuser = g_Confs["s_dbuser"];
	if (g_Confs.find("s_dbpass") != g_Confs.end())
		dbpass = g_Confs["s_dbpass"];
	if (g_Confs.find("s_dbname") != g_Confs.end())
		dbname = g_Confs["s_dbname"];
	string dbport = "5432";
	if (g_Confs.find("s_dbport") != g_Confs.end())
		dbport = g_Confs["s_dbport"];

	g_db = PQsetdbLogin(dbhost.c_str(), dbport.c_str(), NULL, NULL, dbname.c_str(), dbuser.c_str(), dbpass.c_str());

	if(PQstatus(g_db) != CONNECTION_OK)
	{
		ERRO << PQerrorMessage(g_db) << endl;
		PQfinish( g_db);
		g_db = NULL;
		return false;
	}
	string smax, dbfile;
	if (g_Confs.find("maxtable") != g_Confs.end())
		smax = g_Confs["maxtable"];

       int imaxt = atoi(smax.c_str());
        if(imaxt < 1)
           imaxt = 1;

        if (g_Confs.find("dbhost") != g_Confs.end())
                dbhost = g_Confs["dbhost"];
        if (g_Confs.find("dbuser") != g_Confs.end())
                dbuser = g_Confs["dbuser"];
        if (g_Confs.find("dbpass") != g_Confs.end())
                dbpass = g_Confs["dbpass"];
        if (g_Confs.find("dbname") != g_Confs.end())
                dbname = g_Confs["dbname"];

        g_pDB = new CobWebMQL(dbhost.c_str(), dbname.c_str(), dbuser.c_str(), dbpass.c_str(), imaxt);
        if(!g_pDB->Initialize())
             return false;

        string s;
        if (g_Confs.find("uexpire") != g_Confs.end())
		s = g_Confs["uexpire"];
	g_expire = atoi(s.c_str());
	if(g_expire < 10)
		return false;
        if (g_Confs.find("cobweb_port") != g_Confs.end())
		s = g_Confs["cobweb_port"];
	m_cobweb_port = atoi(s.c_str());
	if(m_cobweb_port < 100)
	{
		DEBUG3("cobweb_port = ", m_cobweb_port, " IS ERROR!"); 
		return false;
	}

	return true;
}

int Helper :: Process(map < int, string > &keys, conn *c)
{
	c->wbytes +=  sprintf(c->wcurr, "imok");
	return 0;
}

int Helper :: Process(const char *cmd, conn *c) // char **wbuf, int &wsize, int &wbytes)
{
	DEBUG2("key = ", cmd);
	if(strcmp("__stat__", cmd) == 0)
	{
		c->wbytes +=  sprintf(c->wcurr, "imok");
		return 0;
	}
	else
		return UpdateUser((char *)cmd, c);
}

int Helper :: Timer(long lt, int gap)
{
	return 0;
}


int Helper:: UpdateUser(char *user, conn *c)
{
	c->wbytes +=  sprintf(c->wcurr, "imok");
	string val, name;
	long stamp = 0;
	int iret =  g_pDB->Fetch(user, name, val, stamp);
	if(iret== -1 )
		return 0;

        if(g_db == NULL)
        {
                cerr<<"db connection is NULL"<<endl;
//                exit(-1);
                return -1;
        }

        int u = atoi(user);
        stringstream sql;
        sql <<"select nick, ruid from user_profile where id="<<u;
        PGresult *result = PQexec(g_db, sql.str().c_str());
        if(PQresultStatus(result) != PGRES_TUPLES_OK)
        {
                cerr << PQerrorMessage(g_db) << endl;
                PQclear(result);
                return -1;
        }
        long rownum = PQntuples(result);
        if (rownum <= 0 )
        {
                PQclear(result);
                return 0;
        }
        string sval;
        sql.str("");
        long expire = time(NULL) + g_expire;
        name = (char *)PQgetvalue(result, 0, 0);
        name.append(":");
        name.append((char *)PQgetvalue(result, 0, 1));

        PQclear(result);
        sql.str("");
        sql<<"select u.partnerid, p.nick, p.ruid from user_relation as u, user_profile as p where ownerid ="<<u<<" and class=1 and u.partnerid=p.id";
        DEBUG1(sql.str());
        result = PQexec(g_db, sql.str().c_str());
        if(PQresultStatus(result) != PGRES_TUPLES_OK)
        {
                cerr << PQerrorMessage(g_db) << endl;
                PQclear(result);
                return -1;
        }

        rownum = PQntuples(result);
        DEBUG2("fetch row = ", rownum);
        if (rownum == 0 )
        {
                PQclear(result);
                return 0;
        }
        for (int i = 0; i < rownum; i++)
        {
                sql.str("");
                if(i > 0)
                   sql<<";";
                sql<< (char *)PQgetvalue(result, i, 0) <<":"<< (char *)PQgetvalue(result, i, 1)
                   <<":" << (char *)PQgetvalue(result, i, 2);
                sval.append(sql.str());
        }
        PQclear(result);
        DEBUG1(sval.c_str() );
        if( stamp > 0)
                g_pDB->Replace(user,(char *)name.c_str(), (char *) sval.c_str(), expire);
        else
                g_pDB->Save(user, (char *)name.c_str(), (char *) sval.c_str(), expire);

        Invoke_Server(user);
	return 0;
}


char * Helper :: Stats(char *ext_stats)
{
        string status;
        char hostname[128];
        gethostname(hostname, sizeof(hostname)) ;
        status.append("STAT server ");
        status.append(hostname);
        if(ext_stats != NULL)
                status.append(ext_stats);
        return (char *)status.c_str();
}

bool Helper:: Clear()
{ // ulen +uval + ulen + uval
	return	0;
}

int Helper:: Update(const char *key, int klen, int flags, time_t expire, const char *data, int dlen)
{
	return 0;
}

int Helper :: Check( void *args )
{
	int *port = (int *)args;
	DEBUG1(" begin to Check!");
	memcached_st * memc = memcached_create(NULL);
	memcached_return rc = MEMCACHED_SUCCESS;
	rc = memcached_server_add(memc,(char *)"127.0.0.1",(unsigned int)(*port));
	if (rc != MEMCACHED_SUCCESS){
		perror("Connect to memcache server");
		memcached_free( memc );
		return 0;
	}
	memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_NO_BLOCK, 1);
	memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_CONNECT_TIMEOUT, 1);
	memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_BUFFER_REQUESTS, 0);
	size_t vlen;
	uint32_t flag;
	char *retval = memcached_get(memc,"__stat__", 8, &vlen, &flag, &rc);
	if (rc == MEMCACHED_SUCCESS){
		if (retval != NULL){
			cout<< retval << endl;
			free(retval);
		}
	}
	if (rc != MEMCACHED_SUCCESS)
	{
		printf("Failure to communicate with servers (%s)\n", memcached_strerror(memc, rc));
		for(int i =0; i< 3; i++)
		{
			usleep(100);
			retval = memcached_get(memc, "__stat__", 8, &vlen, &flag, &rc);
			if(rc == MEMCACHED_SUCCESS){
				cout<<retval << endl;
				free(retval);
				break;
			}
		}
		if(rc != MEMCACHED_SUCCESS){
			memcached_free(memc);
			exit(1);
			//abort();
		}
	}
	memcached_free( memc );
	DEBUG1("end Check!");
	return 0;
}

int Helper :: Invoke_Server( char *usr )
{
	memcached_st * memc = memcached_create(NULL);
	memcached_return rc = MEMCACHED_SUCCESS;
	rc = memcached_server_add(memc,(char *)"127.0.0.1",(unsigned int)( m_cobweb_port));
	if (rc != MEMCACHED_SUCCESS){
		perror("Connect to memcache server");
		memcached_free( memc );
		return 0;
	}
	memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_NO_BLOCK, 1);
	memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_CONNECT_TIMEOUT, 1);
	memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_BUFFER_REQUESTS, 0);

	char *keys[3 ];
	size_t key_length[3 ];

        keys[0] = (char *)"reset";
	keys[1] = usr;
	key_length[0] = 5;
	key_length[1] = strlen(usr);
        keys[2] = (char *)"BIND-RESULT";
        key_length[2] = 11;

	memcached_result_st results_obj;
	memcached_result_st *results;
	results= memcached_result_create(memc, &results_obj);
	rc = memcached_mget(memc, keys, key_length, 3);
	memcached_result_free(&results_obj);
	memcached_free( memc );

	return 0;
}

